#include "Selector.h"
#include "Socket.h"
#include "SocketError.hpp"

#include <iostream>
#include <assert.h>
#include <ctime>

namespace nio {

	Selector::Selector() {
		wakener = new Socket(SocketFamily::inet, SocketType::dgram);
		//bind
		int counter = 100;
		int port;
		do {
			try {
				port = 10000 + rand() % 30000;
				wakener->Bind("127.0.0.1", port);
				wakener->Connect("127.0.0.1", port);
				std::cout << "wakener bind port:" << port << std::endl;
				break;
			}
			catch (SocketError& err) {
				std::cout << "Bind wakener error:" << err.toString() << std::endl;
			}
		} while (--counter > 0);
	}

	Selector::~Selector() {
		delete wakener;
		wakener = nullptr;
	}

	void Selector::wakeup() {
		char c = 1;
		wakener->Send(&c, 1);
	}

	void Selector::waitForEvents(int64_t timeout) {
		timeval tv;
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;

		fd_set readSet; FD_ZERO(&readSet);
		fd_set writeSet; FD_ZERO(&writeSet);
		fd_set exceptSet; FD_ZERO(&exceptSet);

		//wakener fd
		SOCKET maxFD = wakener->getfd();
		FD_SET(wakener->getfd(), &readSet);
		//listen fd
		for (auto& it : eventsMap) {
			int fd = it.first;
			int events = it.second;
			if (events == 0)continue;
			if (events & EventSetRead)
				FD_SET(fd, &readSet);
			if (events & EventSetWrite)
				FD_SET(fd, &writeSet);
			if (events & EventSetExcept)
				FD_SET(fd, &exceptSet);

			if (fd > maxFD)maxFD = fd;
		}
#ifdef _WIN32
		maxFD = 0;
#else
		maxFD += 1;
#endif
		int ret = select(maxFD, &readSet, &writeSet, &exceptSet, &tv);
		// select error
		if (ret < 0)assert(false);
		// select timeout
		if (ret == 0) {
			return;
		}

		// is wakener events
		if (FD_ISSET(wakener->getfd(), &readSet)) {
			char c;
			wakener->Recv(&c, 1);
		}
		// have events
		std::map<SOCKET, int>tmpMap;
		for (auto& it : selectableMap) {
			SOCKET fd = it.first;
			int events = 0;
			if (FD_ISSET(fd, &readSet))events |= EventSetRead;
			if (FD_ISSET(fd, &writeSet))events |= EventSetWrite;
			if (FD_ISSET(fd, &exceptSet))events |= EventSetExcept;

			if (events == 0)continue;
			tmpMap[fd] = events;
		}
		if (tmpMap.size() == 0) return;
		for (auto& it : tmpMap) {
			SOCKET fd = it.first;
			int events = it.second;
			auto iter = selectableMap.find(fd);
			if (iter != selectableMap.end()) {
				iter->second->onEvents(events);
			}
		}
	}


	void Selector::addEvents(Selectable* selectable, int events) {
		SOCKET fd = selectable->getFD();
		if (fd == INVALID_FD)return;
		auto it = eventsMap.find(fd);
		if (it == eventsMap.end()) {
			eventsMap[fd] = events;
			selectableMap[fd] = selectable;
		}
		else {
			it->second |= events;
		}
	}

	void Selector::removeEvents(Selectable* selectable, int events) {
		SOCKET fd = selectable->getFD();
		if (fd == INVALID_FD)return;
		auto it = eventsMap.find(fd);
		if (it == eventsMap.end()) {
			return;
		}
		it->second &= (~events);
	}

	void Selector::clearEvents(Selectable* selectable) {
		SOCKET fd = selectable->getFD();
		if (fd == INVALID_FD)return;
		eventsMap.erase(fd);
		selectableMap.erase(fd);
	}


}